package com.hj.springframework2.context;

import com.hj.springframework2.annotation.*;
import com.hj.springframework2.exception.*;
import com.hj.springframework2.io.PropertyResolver;
import com.hj.springframework2.io.ResourceResolver;
import com.hj.springframework2.utils.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.lang.Nullable;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;

/**
 * @Author : hujing
 * @Date: 2025/6/13 14:37
 * @Description:
 */
public class AnnotationConfigApplicationContext implements ConfigurableApplicationContext {
    protected final Logger logger = LoggerFactory.getLogger(getClass());

    protected final PropertyResolver propertyResolver;
    protected final Map<String, BeanDefinition> beans;

    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();
    private Set<String> creatingBeanNames;

    public AnnotationConfigApplicationContext(Class<?> configClass, PropertyResolver propertyResolver) {
        this.propertyResolver = propertyResolver;

        // 扫描获取所有Bean的Class类型
        final Set<String> beanClassNames = scanForClassNames(configClass);

        //创建Bean的定义
        this.beans = createBeanDefinitions(beanClassNames);

        //创建BeanName检测循环依赖
        this.creatingBeanNames = new HashSet<>();

        //创建@Configuration类型的Bean
        this.beans.values().stream()
                .filter(this::isConfigurationDefinition)
                .sorted()
                .map(def -> {
                    createBeanAsEarlySingleton(def);
                    return def.getName();
                }).toList();

        // 创建BeanPostProcessor类型的Bean
        List<BeanPostProcessor> processors =this.beans.values().stream()
                .filter(this::isBeanPostProcessorDefinition)
                .sorted()
                .map(def -> (BeanPostProcessor)createBeanAsEarlySingleton(def)).toList();
        this.beanPostProcessors.addAll(processors);

        //创建其他普通的Bean
        createNormalBeans();

        //打印日志
        this.beans.values().stream().sorted().forEach(def -> logger.info("bean initialized: {}", def));

        // 通过字段或set方法注入依赖
        this.beans.values().forEach(this::injectBean);
    }

    /**
     * 查询def.getBeanClass是否是BeanPostProcessor的子类或接口实现
     *
     * @param def
     * @return
     */
    private boolean isBeanPostProcessorDefinition(BeanDefinition def) {
        return BeanPostProcessor.class.isAssignableFrom(def.getBeanClass());
    }

    /**
     * 注入依赖但不调用init方法
     *
     * @param def
     */
    void injectBean(BeanDefinition def) {
        try {
            injectProperties(def, def.getBeanClass(), def.getInstance());
        } catch (ReflectiveOperationException e) {
            throw new BeanCreationException(e);
        }
    }

    private void injectProperties(BeanDefinition def, Class<?> clazz, Object bean) throws ReflectiveOperationException {
        // 在当前类查找Field何Method并注入
        for (Field f : clazz.getDeclaredFields()) {
            tryInjectProperties(def, clazz, bean, f);
        }
        for (Method m : clazz.getDeclaredMethods()) {
            tryInjectProperties(def, clazz, bean, m);
        }
        // 在父类查找Field和Method并注入
        Class<?> superclass = clazz.getSuperclass();
        if (superclass != null) {
            injectProperties(def, superclass, bean);
        }
    }

    private void tryInjectProperties(BeanDefinition def, Class<?> clazz, Object bean, AccessibleObject acc) throws IllegalAccessException, InvocationTargetException {
        // 获取@Value,@Autowired注解
        Value value = acc.getAnnotation(Value.class);
        Autowired autowired = acc.getAnnotation(Autowired.class);

        if (value == null && autowired == null) {
            return;
        }

        Field field = null;
        Method method = null;
        if (acc instanceof Field f) {
            checkFieldOrMethod(f);
            f.setAccessible(true);
            field = f;
        }
        if (acc instanceof Method m) {
            checkFieldOrMethod(m);
            /**
             * @Autowired
             * public void setBean2(Bean2 bean2){
             *    logger.info("@Autowired注解生效:{}",bean2);
             *    this.bean2 = bean2;
             * }
             */
            if (m.getParameters().length != 1) {
                throw new BeanDefinitionException(
                        String.format("Cannot inject a non-setter method %s for bean '%s': %s", m.getName(), def.getName(), def.getBeanClass().getName()));
            }
            m.setAccessible(true);
            method = m;
        }

        String accessibleName = field != null ? field.getName() : method.getName();
        Class<?> accessibleType = field != null ? field.getType() : method.getParameterTypes()[0];

        if (value != null && autowired != null) {
            throw new BeanCreationException(String.format("Cannot specify both @Autowired and @Value when inject %s.%s for bean '%s': %s",
                    clazz.getSimpleName(), accessibleName, def.getName(), def.getBeanClass().getName()));
        }

        //@Value注入
        if (value != null) {
            Object propValue = this.propertyResolver.getProperty(value.value(), accessibleType);
            if (field != null) {
                logger.info("Field injection: {}.{} = {}", def.getBeanClass().getName(), accessibleName, propValue);
                field.set(bean, propValue);
            }
            if (method != null) {
                logger.info("Method injection: {}.{} ({})", def.getBeanClass().getName(), accessibleName, propValue);
                method.invoke(bean, propValue);
            }
        }

        //@Autowired注入
        if (autowired != null) {
            String name = autowired.name();
            boolean required = autowired.value();
            Object depends = name.isEmpty() ? findBean(accessibleType) : findBean(name, accessibleType);
            if (required && depends == null) {
                throw new UnsatisfiedDependencyException(String.format("Dependency bean not found when inject %s.%s for bean '%s': %s", clazz.getSimpleName(),
                        accessibleName, def.getName(), def.getBeanClass().getName()));
            }
            if (depends != null) {
                if (field != null) {
                    logger.info("Field injection: {}.{} = {}", def.getBeanClass().getName(), accessibleName, depends);
                    field.set(bean, depends);
                }
                if (method != null) {
                    logger.info("field injection: {}.{} ({})", def.getBeanClass().getName(), accessibleName, depends);
                    method.invoke(bean, depends);
                }
            }
        }
    }

    @Nullable
    public <T> T findBean(String name, Class<T> requiredType) {
        BeanDefinition def = findBeanDefinition(name, requiredType);
        if (def == null) {
            return null;
        }
        return (T) def.getRequiredInstance();
    }

    @Nullable
    protected <T> T findBean(Class<T> requiredType) {
        BeanDefinition beanDefinition = findBeanDefinition(requiredType);
        if (beanDefinition == null) {
            return null;
        }
        return (T) beanDefinition.getRequiredInstance();
    }

    /**
     * 检查Field或Method是否符合要求,rules:
     * 1.属性或方法不能是static的
     * 2.属性或方法不能是final的,如果是final,则会在运行时抛出NullPointerException
     *
     * @param m
     */
    void checkFieldOrMethod(Member m) {
        int mod = m.getModifiers();
        // 判断属性是否是static的,如果是,则抛出异常
        if (Modifier.isStatic(mod)) {
            throw new BeanDefinitionException("Cannot inject static field: " + m);
        }
        if (Modifier.isFinal(mod)) {
            if (m instanceof Field field) {
                throw new BeanDefinitionException("Cannot inject final field: " + field);
            }
            if (m instanceof Method method) {
                logger.warn(
                        "Inject final method should be careful because it is not called on target bean when bean is proxied and may cause NullPointerException.");
            }
        }
    }

    void createNormalBeans() {
        // 获取BeanDefinition的列表
        List<BeanDefinition> defs = this.beans.values().stream().filter(def -> def.getInstance() == null)
                .sorted()
                .toList();
        // 创建Bean
        for (BeanDefinition def : defs) {
            // 如果Bean未被创建(可能在其他Bean的构造方法注入前被创建):
            if (def.getInstance() == null) {
                // 创建Bean
                createBeanAsEarlySingleton(def);
            }
        }
    }

    /**
     * 根据BeanDefinition创建单例Bean
     * 1.先获取参数,如果参数是@Value注解标注,则从PropertyResolver中获取值
     * 2.如果参数是@Autowired注解标注,则从BeanDefinitionMap中获取依赖的Bean,如果依赖的Bean尚未初始化,则递归调用初始化该依赖Bean
     * 3.创建Bean实例,并设置到BeanDefinition的instance属性中
     *
     * @param def
     * @return
     */
    public Object createBeanAsEarlySingleton(BeanDefinition def) {
        logger.info("Try create bean '{}' as early singleton: {}", def.getName(), def.getBeanClass().getName());
        if (!this.creatingBeanNames.add(def.getName())) {
            throw new UnsatisfiedDependencyException(String.format("Circular dependency detected when create bean '%s'", def.getName()));
        }

        // 创建方式:构造方法或工厂方法
        Executable createFn;
        if (def.getFactoryName() == null) {
            // 构造方法创建
            createFn = def.getConstructor();
        } else {
            // 工厂方法创建
            createFn = def.getFactoryMethod();
        }

        // 创建参数
        final Parameter[] parameters = createFn.getParameters();
        final Annotation[][] parameterAnnos = createFn.getParameterAnnotations();
        Object[] args = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            final Parameter param = parameters[i];
            final Annotation[] paramAnnos = parameterAnnos[i];
            final Value value = ClassUtils.getAnnotation(paramAnnos, Value.class);
            final Autowired autowired = ClassUtils.getAnnotation(paramAnnos, Autowired.class);

            // @Configuration类型的Bean是工厂，不允许使用@Autowired创建
            final boolean isConfiguration = isConfigurationDefinition(def);
            if (isConfiguration && autowired != null) {
                throw new BeanCreationException(
                        String.format("Cannot specify @Autowired when create @Configuration bean '%s': %s.", def.getName(), def.getBeanClass().getName()));
            }

            final boolean isBeanPostProcessor = isBeanPostProcessorDefinition(def);
            if (isBeanPostProcessor && autowired != null) {
                throw new BeanCreationException(
                        String.format("Cannot specify @Autowired when create BeanPostProcessor '%s': %s.", def.getName(), def.getBeanClass().getName()));
            }
            
            // 参数需要@Value或@Autowired两者之一
            if (value != null && autowired != null) {
                throw new BeanCreationException(
                        String.format("Cannot specify both @Autowired and @Value when create bean '%s': %s.", def.getName(), def.getBeanClass().getName()));
            }
            if (value == null && autowired == null) {
                throw new BeanCreationException(
                        String.format("Must specify @Autowired or @Value when create bean '%s': %s.", def.getName(), def.getBeanClass().getName()));
            }
            // 参数类型:
            final Class<?> type = param.getType();
            if (value != null) {
                // 参数是@Value:
                args[i] = this.propertyResolver.getRequiredProperty(value.value(), type);
            } else {
                // 参数是@Autowired:
                String name = autowired.name();
                boolean required = autowired.value();
                // 依赖的BeanDefinition,没有name则根据类型查找,否则根据name查找
                BeanDefinition dependsOnDef = name.isEmpty() ? findBeanDefinition(type) : findBeanDefinition(name, type);
                // 检测required==true?
                if (required && dependsOnDef == null) {
                    throw new BeanCreationException(String.format("Missing autowired bean with type '%s' when create bean '%s': %s.", type.getName(),
                            def.getName(), def.getBeanClass().getName()));
                }
                if (dependsOnDef != null) {
                    // 获取依赖的Bean
                    Object autowiredBeanInstance = dependsOnDef.getInstance();
                    if (autowiredBeanInstance == null && !isConfiguration) {
                        // 当前依赖Bean尚未初始化，递归调用初始化该依赖Bean:
                        autowiredBeanInstance = createBeanAsEarlySingleton(dependsOnDef);
                    }
                    args[i] = autowiredBeanInstance;
                } else {
                    args[i] = null;
                }
            }
        }

        // 创建Bean实例:
        Object instance;
        if (def.getFactoryName() == null) {
            // 用构造方法创建
            try {
                instance = def.getConstructor().newInstance(args);
            } catch (Exception e) {
                throw new BeanCreationException(String.format("Exception when create bean '%s': %s", def.getName(), def.getBeanClass().getName()), e);
            }
        } else {
            // 用@Bean方法创建
            Object configInstance = getBean(def.getFactoryName());
            try {
                instance = def.getFactoryMethod().invoke(configInstance, args);
            } catch (Exception e) {
                throw new BeanCreationException(String.format("Exception when create bean '%s': %s", def.getName(), def.getBeanClass().getName()), e);
            }
        }
        def.setInstance(instance);
        
        // 调用BeanPostProcessor处理Bean
        for (BeanPostProcessor processor : beanPostProcessors) {
            Object processed = processor.postProcessBeforeInitialization(def.getInstance(),def.getName());
            if (processed == null) {
                throw new BeanCreationException(String.format("PostBeanProcessor returns null when process bean '%s' by %s", def.getName(), processor));
            }
            //如果一个BeanPostProcessor替换了原始Bean,则更新Bean的引用
            if (def.getInstance() != processed){
                logger.info("Bean '{}' was replaced by post processor {}.", def.getName(), processor.getClass().getName());
                def.setInstance(processed);
            }
        }
        return def.getInstance();
    }

    @Override
    public boolean containsBean(String name) {
        return this.beans.containsKey(name);
    }

    public <T> T getBean(String name) {
        BeanDefinition def = this.beans.get(name);
        if (def == null) {
            throw new NoSuchBeanDefinitionException(String.format("No bean defined with name '%s'.", name));
        }
        return (T) def.getRequiredInstance();
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) {
        T t = findBean(name,requiredType);
        if (t == null){
            throw new NoSuchBeanDefinitionException(String.format("No bean defined with name '%s' and type '%s'.", name, requiredType));
        }
        return t;
    }

    @Override
    public <T> T getBean(Class<T> requiredType) {
        T t = findBean(requiredType);
        if (t == null){
            throw new NoSuchBeanDefinitionException(String.format("No bean defined with type '%s'.", requiredType));
        }
        return t;
    }

    @Override
    public <T> List<T> getBeans(Class<T> requiredType) {
        List<BeanDefinition> defs = findBeanDefinitions(requiredType);
        if (defs.isEmpty()){
            return List.of();
        }
        List<T> list = new ArrayList<>();
        for (BeanDefinition def : defs) {
            list.add((T) def.getRequiredInstance());
        }
        return list;
    }

    @Override
    public void close() {

    }

    public BeanDefinition findBeanDefinition(String name, Class<?> requiredType) {
        BeanDefinition def = findBeanDefinition(name);
        if (def == null) {
            return null;
        }
        if (!requiredType.isAssignableFrom(def.getBeanClass())) {
            throw new BeanNotOfRequiredTypeException(String.format("Autowire required type '%s' but bean '%s' has actual type '%s'.", requiredType.getName(),
                    name, def.getBeanClass().getName()));
        }
        return def;
    }

    public BeanDefinition findBeanDefinition(String name) {
        return this.beans.get(name);
    }

    /**
     * 根据Type查询某个BeanDefinition,如果不存在返回null
     * 如果存在多个,则返回@Primary标注的那个
     *
     * @param type
     * @return
     */
    public BeanDefinition findBeanDefinition(Class<?> type) {
        List<BeanDefinition> defs = findBeanDefinitions(type);
        if (defs.isEmpty()) {
            return null;
        }
        if (defs.size() == 1) {
            return defs.get(0);
        }
        //有多个BeanDefinition,查找@Primary标注的那个
        List<BeanDefinition> primaryDefs = defs.stream().filter(def -> def.isPrimary()).toList();
        if (primaryDefs.size() == 1) {
            return primaryDefs.get(0);
        }
        if (primaryDefs.isEmpty()) {
            throw new NoUniqueBeanDefinitionException(String.format("Multiple bean with type '%s' found, but no @Primary specified.", type.getName()));
        } else {
            throw new NoUniqueBeanDefinitionException(String.format("Multiple primary bean with type '%s' found.", type.getName()));
        }
    }

    /**
     * isAssignableFrom用于判断当前类（或接口）是否可以被赋值给指定的类（或接口），即检查类之间的兼容性关系,即def.getBeanClass()是type的子类或接口实现
     *
     * @param type
     * @return
     */
    public List<BeanDefinition> findBeanDefinitions(Class<?> type) {
        return this.beans.values().stream()
                .filter(def -> type.isAssignableFrom(def.getBeanClass()))
                .sorted()
                .toList();
    }

    boolean isConfigurationDefinition(BeanDefinition def) {
        return ClassUtils.findAnnotation(def.getBeanClass(), Configuration.class) != null;
    }

    /**
     * 根据扫描的className创建BeanDefinition
     *
     * @param classNameSet
     * @return
     */
    private Map<String, BeanDefinition> createBeanDefinitions(Set<String> classNameSet) {
        Map<String, BeanDefinition> defs = new HashMap<>();
        for (String className : classNameSet) {
            //获取Class
            Class<?> clazz;
            try {
                clazz = Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
            if (clazz.isAnnotation() || clazz.isEnum() || clazz.isInterface()) {
                continue;
            }
            //是否标准了@Component注解
            Component component = AnnotationUtils.findAnnotation(clazz, Component.class);
            if (component != null) {
                logger.info("found component: " + clazz.getName());

                //判断是否是抽象类或者私有类
                int mod = clazz.getModifiers();
                if (Modifier.isAbstract(mod)) {
                    logger.error("@Component class " + clazz.getName() + " must not be abstract.");
                }
                if (Modifier.isPrivate(mod)) {
                    logger.error("@Component class " + clazz.getName() + " must not be private.");
                }
                String beanName = ClassUtils.getBeanName(clazz);
                BeanDefinition bd = new BeanDefinition(beanName, clazz, getSuitableConstructor(clazz), getOrder(clazz), clazz.isAnnotationPresent(Primary.class),
                        // named init / destroy method:
                        null, null,
                        // init method
                        ClassUtils.findAnnotionMethod(clazz, PostConstruct.class),
                        // destroy method
                        ClassUtils.findAnnotionMethod(clazz, PreDestroy.class)
                );
                defs.put(bd.getName(), bd);
                logger.info("define bean: " + bd);

                Configuration configuration = ClassUtils.findAnnotation(clazz, Configuration.class);
                if (configuration != null) {
                    scanFactoryMethods(beanName, clazz, defs);
                }
            }
        }
        return null;
    }

    /**
     * Scan factory method that annotated with @Bean:
     *
     * <code>
     *
     * @Configuration public class Hello {
     * @Bean ZoneId createZone() {
     * return ZoneId.of("Z");
     * }
     * }
     * </code>
     */
    private void scanFactoryMethods(String factoryBeanName, Class<?> clazz, Map<String, BeanDefinition> defs) {
        for (Method method : clazz.getDeclaredMethods()) {
            Bean bean = method.getAnnotation(Bean.class);
            if (bean != null) {
                int mod = method.getModifiers();
                //判断是否是抽象方法
                if (Modifier.isAbstract(mod)) {
                    throw new BeanDefinitionException("@Bean method " + clazz.getName() + "." + method.getName() + " must not be abstract.");
                }
                //判断是否是final方法
                if (Modifier.isFinal(mod)) {
                    throw new BeanDefinitionException("@Bean method " + clazz.getName() + "." + method.getName() + " must not be final.");
                }
                //判断是否是私有方法
                if (Modifier.isPrivate(mod)) {
                    throw new BeanDefinitionException("@Bean method " + clazz.getName() + "." + method.getName() + " must not be private.");
                }
                //获取方法的返回类型,返回类型就是BeanDefinition的声明类型
                Class<?> beanClass = method.getReturnType();
                //判断返回类型是否是原始类型
                if (beanClass.isPrimitive()) {
                    throw new BeanDefinitionException("@Bean method " + clazz.getName() + "." + method.getName() + " must not be primitive.");
                }
                if (beanClass == void.class || beanClass == Void.class) {
                    throw new BeanDefinitionException("@Bean method " + clazz.getName() + "." + method.getName() + " must not be void.");
                }
                BeanDefinition def = new BeanDefinition(ClassUtils.getBeanName(method), beanClass, factoryBeanName, method, getOrder(method),
                        method.isAnnotationPresent(Primary.class),
                        //init method
                        bean.initMethod().isEmpty() ? null : bean.initMethod(),
                        //destroy method
                        bean.destroyMethod().isEmpty() ? null : bean.destroyMethod(),
                        // @PostConstruct / @PreDestroy method:
                        null, null
                );
                addBeanDefinition(defs, def);
                logger.info("define bean: {}", def);
            }
        }
    }

    private void addBeanDefinition(Map<String, BeanDefinition> defs, BeanDefinition def) {
        if (defs.put(def.getName(), def) != null) {
            throw new BeanDefinitionException("Duplicate bean name: " + def.getName());
        }
    }

    int getOrder(Method method) {
        com.hj.springframework2.annotation.Order order = method.getAnnotation(com.hj.springframework2.annotation.Order.class);
        return order == null ? Integer.MAX_VALUE : order.value();
    }

    int getOrder(Class<?> clazz) {
        Order order = clazz.getAnnotation(Order.class);
        return order == null ? Integer.MAX_VALUE : order.value();
    }

    /**
     * 获取一个合适的构造方法
     *
     * @param clazz
     * @return
     */
    Constructor<?> getSuitableConstructor(Class<?> clazz) {
        //获取类的公共构造方法
        Constructor<?>[] cons = clazz.getConstructors();
        if (cons.length == 0) {
            //获取所有构造方法
            cons = clazz.getDeclaredConstructors();
            if (cons.length != 1) {
                throw new BeanDefinitionException("More than one constructor found in class " + clazz.getName() + ".");
            }
        }
        if (cons.length != 1) {
            throw new BeanDefinitionException("More than one public constructor found in class " + clazz.getName() + ".");
        }
        return cons[0];
    }

    private Set<String> scanForClassNames(Class<?> configClass) {
        ComponentScan scan = AnnotationUtils.findAnnotation(configClass, ComponentScan.class);
        // 获取扫描的包,如果没有指定,则默认扫描当前类的包
        String[] basePackages = scan == null || scan.value().length == 0 ? new String[]{configClass.getPackage().getName()} : scan.value();

        Set<String> classNameSet = new HashSet<>();
        for (String basePackage : basePackages) {
            logger.info("scanning package: " + basePackage);
            ResourceResolver rr = new ResourceResolver(basePackage);
            List<String> classList = rr.scan(res -> {
                String name = res.getName();
                if (name.endsWith(".class")) {
                    return name.substring(0, name.length() - 6).replace("/", ".").replace("\\", ".");
                }
                return null;
            });
            classNameSet.addAll(classList);
        }

        // 查找@Import(Xyz.class):
        Import importConfig = AnnotationUtils.findAnnotation(configClass, Import.class);
        if (importConfig != null) {
            for (Class<?> aClass : importConfig.value()) {
                String name = aClass.getName();
                if (classNameSet.contains(name)) {
                    logger.warn("ignore import: " + name + " for it is already been scanned.");
                } else {
                    classNameSet.add(name);
                    logger.info("add import: " + name);
                }
            }
        }
        return classNameSet;
    }

}
